home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
muds
/
pennmush.000
/
pennmush-1.50-p8-linux.tar
/
pennmush
/
smalloc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-04
|
15KB
|
672 lines
#ifdef USE_SMALLOC
/*
Satoria's Malloc Package 1.2
*/
#include <stdio.h>
#include <memory.h>
#include "config.h"
#include "externs.h"
#define MTYPE unsigned int
#define SMALL_BLOCK_MAX_BYTES 32
#define SMALL_CHUNK_SIZE 0x4000
#define CHUNK_SIZE 0x40000
#define SINT sizeof(int)
#define SMALL_BLOCK_MAX (SMALL_BLOCK_MAX_BYTES/SINT)
#define PREV_BLOCK 0x80000000
#define THIS_BLOCK 0x40000000
#define MASK 0x0FFFFFFF
typedef unsigned int u;
/*
SMALL BLOCK data structures
*/
static u *sfltable[SMALL_BLOCK_MAX]; /* freed list */
static u *next_unused;
static u unused_size; /* until we need a new chunk */
/*
LARGE BLOCK data structures
*/
static u *free_list;
static u *start_next_block;
/*
STATISTICS
*/
static int small_count[SMALL_BLOCK_MAX];
static int small_total[SMALL_BLOCK_MAX];
typedef struct { unsigned counter; unsigned long size; } stat;
#ifndef SLOW_STATISTICS
#define COUNT(a,b)
#define START(a)
#else
#define COUNT(a,b) \
{ \
a.size+=(b); \
if ((b)<0) \
--a.counter; \
else \
++a.counter; \
}
#define START(a) a.size=0; a.counter=0;
#endif
#ifdef DEBUG
int debugmalloc = 1; /* Only used when debuging malloc() */
#else
int debugmalloc = 0;
#endif
/********************************************************/
/* SMALL BLOCK HANDLER */
/********************************************************/
char *large_malloc();
static void large_free();
#define ptr_to_smallblock_size_field(p) (p)
#define next_free_smallblock(p) ((u **) (p+1))
stat small_alloc_stat, /* amount allocated */
small_free_stat, /* amount unused */
small_total_stat, /* total ever requested */
small_chunk_stat; /* small chunks allocated */
void *malloc(sz)
MTYPE sz;
{
u *temp,i,size;
if (sz == 0) {
fprintf(stderr, "Malloc size 0.\n");
return NULL;
}
if (sz>SMALL_BLOCK_MAX_BYTES)
return large_malloc((u) sz,0);
/* Compute the index into the small block table:
index block size
0 1-4 bytes
1 5-8 bytes
2 9-12 bytes
*/
i = (sz - 1) >> 2;
/* Compute the actual size of the small block in ints,
including the one int overhead for the "header"
*/
size = (sz + 3) >> 2; /* block size in ints */
++size; /* one int overhead */
/* Update statistics of small block allocations */
COUNT(small_alloc_stat,size << 2);
COUNT(small_total_stat,size << 2);
small_count[i] += 1; /* update statistics */
small_total[i] += 1;
/* Is the free list for this size of block non-empty? */
if (sfltable[i]) {
/* Update the free list stats */
COUNT(small_free_stat, -(int) (size << 2));
temp = sfltable[i];
sfltable[i] = * (u **) (temp+1);
return (char *) (temp+1);
}
/* Free list was empty. So allocate from the small-block chunk. */
if (unused_size<size) {
/* There's not enough room in this chunk. Blow it off (wasting the
space at the end of the chunk), and get a new one. */
next_unused = (u *) large_malloc(SMALL_CHUNK_SIZE,1);
if (next_unused == 0)
return 0;
COUNT(small_chunk_stat, SMALL_CHUNK_SIZE+SINT);
unused_size = SMALL_CHUNK_SIZE / SINT;
}
temp = (u *) next_free_smallblock(next_unused);
*ptr_to_smallblock_size_field(next_unused) = size;
next_unused += size;
unused_size -= size;
return (char *) temp;
}
void free(ptr)
char *ptr;
{
u *block;
u i;
/* Find the header for this block */
block = (u *) ptr;
block -= 1;
/* Test the size of this block to see if it's large or small */
if ( (*ptr_to_smallblock_size_field(block) & MASK)
> SMALL_BLOCK_MAX + 1) {
large_free(ptr);
return;
}
/* Get index for this size of block. There's no & MASK because
small block headers only have the size
*/
i = *block - 2;
/* Update the statistics */
COUNT(small_alloc_stat, - (int) ((i+2) << 2));
COUNT(small_free_stat, (i+2) << 2);
small_count[i] -= 1;
*next_free_smallblock(block) = sfltable[i];
sfltable[i] = block;
}
/************************************************/
/* LARGE BLOCK HANDLER */
/************************************************/
#define BEST_FIT 0
#define FIRST_FIT 1
#define HYBRID 2
int fit_style = BEST_FIT;
#define ptr_to_largeblock_size_field(p) (p)
#define next_free_largeblock(p) (*((u **) (p+1)))
#define previous_free_largeblock(p) (*((u **) (p+2)))
#define next_large_block(p) (p + (*(p) & MASK))
#define previous_largeblock(p) (p - (*(p-1)))
#define is_previous_free(p) (!(*p & PREV_BLOCK))
#define is_next_free(p) (!(*next_large_block(p) & THIS_BLOCK))
#ifdef DEBUG
static void show_block(ptr)
u *ptr;
{
fprintf(stderr, "MALLOC: [%c%d: %d]\n",(*ptr & THIS_BLOCK ? '+' : '-'),
(int) ptr, *ptr & MASK);
}
void show_free_list()
{
u *p;
p = free_list;
while (p) {
show_block(p);
p = next_free_largeblock(p);
}
}
#endif
stat large_free_stat;
stat large_free_chain;
void remove_from_free_list(ptr)
u *ptr;
{
COUNT(large_free_stat, - (int) ((*ptr & MASK) << 2));
if (previous_free_largeblock(ptr))
next_free_largeblock(previous_free_largeblock(ptr))
= next_free_largeblock(ptr);
else
free_list
= next_free_largeblock(ptr);
if (next_free_largeblock(ptr))
previous_free_largeblock(next_free_largeblock(ptr))
= previous_free_largeblock(ptr);
}
void add_to_free_list(ptr)
u *ptr;
{
COUNT(large_free_stat, (*ptr & MASK) << 2);
if (free_list && previous_free_largeblock(free_list))
fprintf(stderr, "ERROR: Free list consistency error.\n");
next_free_largeblock(ptr) = free_list;
if (free_list)
previous_free_largeblock(free_list) = ptr;
previous_free_largeblock(ptr) = 0;
free_list = ptr;
}
/* build a properly annotated unallocated block */
static void build_block(ptr, size)
u *ptr,size;
{
/* Set the header to: (old value of PREV_BLOCK) | !THIS_BLOCK | size */
*(ptr) = (*ptr & PREV_BLOCK) | size;
/* Set the footer (immediately before the next blocks' header) to the size */
*(ptr+size-1) = size;
/* Clear PREV_BLOCK on the next block */
*(ptr+size) &= (MASK | THIS_BLOCK);
}
static void mark_block(ptr)
u *ptr;
{
*ptr |= THIS_BLOCK;
*next_large_block(ptr) |= PREV_BLOCK;
}
/*
mod by Lars Pensj| (??):
It is system dependent how sbrk() aligns data, so we simply
use brk() to insure that we have enough.
*/
stat sbrk_stat;
static char *esbrk(size)
u size;
{
extern char *sbrk();
extern int brk();
static char *current_break;
if (current_break == 0)
current_break = sbrk(0);
if (brk(current_break + size) == -1)
return 0;
COUNT(sbrk_stat,size);
current_break += size;
return current_break - size;
}
stat large_alloc_stat;
stat large_total_stat;
char *large_malloc(size, force_more)
u size;
int force_more;
{
u best_size, real_size;
u *first, *best, *ptr;
/* Round the block size up to a multiple of 4, then divide by four, and add
1, to get the actual block size in ints, including room for the header
*/
size = (size + 7) >> 2;
first = best = 0;
best_size = MASK;
/* If we are being forced to allocate a big block, ignore the fit attempts */
if (force_more)
ptr = 0;
else {
ptr = free_list;
#ifdef SLOW_STATISTICS
large_free_chain.counter++;
#endif
}
/* run through the free list, looking for a perfect, first, or best fit */
while (ptr) {
u tempsize;
#ifdef SLOW_STATISTICS
large_free_chain.size++;
#endif
/* Perfect fit? */
tempsize = *ptr & MASK;
if (tempsize == size) {
best = first = ptr;
break; /* always accept perfect fit */
}
/* If allocating this block for this malloc call would result in a hole
that's smaller than the minimum large block size, that is, smaller
than than SMALL_BLOCK_MAX, then it'd never get allocated. So refuse
those cases.
*/
#ifndef WASTE_SMALL
if (tempsize >= size + SMALL_BLOCK_MAX + 1) {
#else
if (tempsize >= size) {
#endif
if (!first) {
first = ptr;
if (fit_style == FIRST_FIT)
/* If we're using first fit, just take this one! */
break;
}
/* Find out how well this one fits */
tempsize -= size;
if (tempsize>0 && tempsize<=best_size) {
best = ptr;
best_size = tempsize;
}
}
ptr = next_free_largeblock(ptr);
} /* end while */
if (fit_style==BEST_FIT)
ptr = best;
else
ptr = first; /* Hybrid says use first if no perfect */
if (!ptr) {
/* There was no match, or we're forced, so allocate more memory */
u chunk_size, block_size;
block_size = size*SINT;
if (force_more || (block_size>CHUNK_SIZE))
chunk_size = block_size;
else
chunk_size = CHUNK_SIZE;
if (!start_next_block) {
/* This is the first allocation */
start_next_block = (u *) esbrk(SINT);
if (!start_next_block)
return 0;
/* The first block header must mark the previous block as used */
*(start_next_block) = PREV_BLOCK;
/* Initialize statistics */
START(large_alloc_stat)
START(large_total_stat)
START(large_free_stat)
START(sbrk_stat)
START(small_alloc_stat)
START(small_free_stat)
START(small_total_stat)
START(small_chunk_stat)
START(large_free_chain)
}
ptr = (u *) esbrk(chunk_size);
if (ptr == 0)
return 0;
/* Old block at end of memory had an extra trailing word. Overwrite it. */
ptr -= 1;
block_size = chunk_size / SINT;
/* Configure the header information for this new bit. */
build_block(ptr,block_size);
/* Set up the bogus header at the end of memory */
*next_large_block(ptr)=THIS_BLOCK;
/* Stick this new block in the free list, so it's exactly like a real
free block, so it looks the same as one found in the free list
*/
add_to_free_list(ptr);
}
/* Ok, we got the free block of appropriate size from somewhere */
remove_from_free_list(ptr);
real_size = *ptr & MASK;
if (real_size != size) {
/* split block pointed to by ptr into two blocks */
build_block(ptr+size, real_size-size);
add_to_free_list(ptr+size);
build_block(ptr, size);
}
mark_block(ptr);
/* Update statistics */
COUNT(large_alloc_stat, size << 2);
COUNT(large_total_stat, size << 2);
return (char *) (ptr + 1);
}
static void large_free(ptr)
char *ptr;
{
u size, *p;
/* Find the header again */
p = (u *) ptr;
p-=1;
/* Update statistics */
size = *p & MASK;
COUNT(large_alloc_stat, - (int) (size << 2));
/* Since this is about to become a hole, check memory before and after
for holes. If they are holes, merge them with this one.
*/
if (is_next_free(p)) {
remove_from_free_list(next_large_block(p));
size += (*next_large_block(p) & MASK);
*p = (*p & PREV_BLOCK) | size;
}
if (is_previous_free(p)) {
remove_from_free_list(previous_largeblock(p));
size += (*previous_largeblock(p) & MASK);
p = previous_largeblock(p);
}
build_block(p, size);
add_to_free_list(p);
}
void *realloc(p, size)
char *p; unsigned size;
{
unsigned *q,old_size;
char *t;
q = ((unsigned *) p)-1;
old_size = ((*q & MASK) -1) * SINT;
if (old_size >= size)
return p;
t = malloc(size);
if (t == 0) return (char *) 0;
memcpy(t, p, old_size);
free(p);
return t;
}
int resort_free_list() { return 0; }
#define d(str,stat) p(player, tprintf(str,stat.counter,stat.size))
#define p notify
#ifdef SLOW_STATISTICS
void dump_malloc_data(player)
dbref player;
{
p(player, "TOTAL ALLOCATIONS (all requests ever made)" );
p(player, " Type Count Space (bytes)" );
d(" large blocks: %9d %12ld", large_total_stat);
d(" small blocks: %9d %12ld", small_total_stat);
d(" sbrk requests: %8d %10ld (a)",sbrk_stat);
if (large_free_chain.counter)
p(player, tprintf(" large mallocs: %9d average search length: %7.2f",
large_free_chain.counter,
(double) large_free_chain.size/large_free_chain.counter));
p(player, "CURRENT USAGE");
p(player, " Type Count Space (bytes)" );
d(" large blocks: %8d %10ld (b)", large_alloc_stat);
d(" large holes: %8d %10ld (c)", large_free_stat);
d(" small chunks: %8d %10ld (d)", small_chunk_stat);
d(" small blocks: %8d %10ld (e)", small_alloc_stat);
d(" small holes: %8d %10ld (f)", small_free_stat);
p(player, tprintf(" unused from current chunk %10d (g)",
unused_size<<2));
p(player, "NOTES");
p(player, " Small blocks are stored in small chunks, which are allocated as");
p(player,"large blocks. Therefore, the total large blocks allocated (b) plus");
p(player,"the large free blocks (c) should equal total storage from sbrk (a).");
p(player,"Similarly, (e) + (f) + (g) equals (d). The total amount of storage");
p(player,"wasted is (c) + (f) + (g); the amount allocated is (b) - (f) - (g).");
}
#endif
#undef p
#undef d
/*
Modified by Lars Pensj| (??)
calloc() is provided because some stdio packages uses it.
*/
void *calloc(nelem, sizel)
unsigned nelem, sizel;
{
char *p;
if (nelem == 0 || sizel == 0)
return 0;
p = malloc(nelem * sizel);
if (p == 0)
return 0;
(void) memset(p, 0, nelem * sizel);
return p;
}
/*
* Functions below can be used to debug malloc.
*/
#ifdef DEBUG
unsigned int memused() { return sbrk_stat.size; }
/*
* Verify that the free list is correct. The upper limit compared to
* is very machine dependant.
*/
verify_sfltable(player)
dbref player;
{
u *p;
int i, j;
extern int end;
if (!debugmalloc)
return;
if (unused_size > SMALL_CHUNK_SIZE / SINT) {
fprintf(stderr, "ERROR: free list. SMALL_CHUNK/SINT < unused");
notify(player, "ERROR: free list. SMALL_CHUNK/SINT < unused");
}
for (i=0; i < SMALL_BLOCK_MAX; i++) {
for (j=0, p = sfltable[i]; p; p = * (u **) (p + 1), j++) {
if (p < (u *)&end || p > (u *) 0xfffff) {
fprintf(stderr, "ERROR: free list. pointer out of range.");
notify(player, "ERROR: free list. pointer out of range.");
}
if (*p - 2 != i) {
fprintf(stderr, "ERROR: free list. pointer corrupt.");
notify(player, "ERROR: free list. pointer corrupt.");
}
}
if (p >= next_unused && p < next_unused + unused_size) {
notify(player, "ERROR: free list. pointer in bad place.");
fprintf(stderr, "ERROR: free list. pointer in bad place.");
}
}
p = free_list;
while (p) {
if (p >= next_unused && p < next_unused + unused_size) {
notify(player, "ERROR: free list. pointer in bad place.");
fprintf(stderr, "ERROR: free list. pointer in bad place.");
}
p = next_free_largeblock(p);
}
}
verify_free(ptr)
u *ptr;
{
u *p;
int i, j;
if (!debugmalloc)
return;
for (i=0; i < SMALL_BLOCK_MAX; i++) {
for (j=0, p = sfltable[i]; p; p = * (u **) (p + 1), j++) {
if (*p - 2 != i)
fprintf(stderr, "ERROR: error in free list");
if (ptr >= p && ptr < p + *p)
fprintf(stderr, "ERROR: error in free list");
if (p >= ptr && p < ptr + *ptr)
fprintf(stderr, "ERROR: error in free list");
if (p >= next_unused && p < next_unused + unused_size)
fprintf(stderr, "ERROR: error in free list");
}
}
p = free_list;
while (p) {
if (ptr >= p && ptr < p + (*p & MASK))
fprintf(stderr, "ERROR: error in free list");
if (p >= ptr && p < ptr + (*ptr & MASK))
fprintf(stderr, "ERROR: error in free list");
if (p >= next_unused && p < next_unused + unused_size)
fprintf(stderr, "ERROR: error in free list");
p = next_free_largeblock(p);
}
if (ptr >= next_unused && ptr < next_unused + unused_size)
fprintf(stderr, "ERROR: error in free list");
}
#endif /* DEBUG */
#endif /* USE_SMALLOC */